package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.production.ProductionCertDto;
import com.example.supplychainbackend.controller.dto.production.ProductionProcessDto;
import com.example.supplychainbackend.controller.dto.production.ProductionSchedulingDto;
import com.example.supplychainbackend.controller.dto.production.ProductionWorkorderDto;
import com.example.supplychainbackend.dao.production.ProductionCertDao;
import com.example.supplychainbackend.dao.production.ProductionProcessDao;
import com.example.supplychainbackend.dao.production.ProductionSchedulingDao;
import com.example.supplychainbackend.dao.production.ProductionWorkorderDao;
import com.example.supplychainbackend.entity.production.ProductionCertEntity;
import com.example.supplychainbackend.entity.production.ProductionProcessEntity;
import com.example.supplychainbackend.entity.production.ProductionSchedulingEntity;
import com.example.supplychainbackend.entity.production.ProductionWorkorderEntity;
import com.example.supplychainbackend.mapper.ProductionMapper;
import com.example.supplychainbackend.service.BlockchainService;
import com.example.supplychainbackend.service.PlanService;
import com.example.supplychainbackend.service.ProductionService;
import com.example.supplychainbackend.tool.DateTimeUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class ProductionServiceImpl implements ProductionService {
    private final PlanService planService;

    private final ProductionCertDao productionCertDao;
    private final ProductionWorkorderDao productionWorkorderDao;
    private final ProductionSchedulingDao productionSchedulingDao;
    private final ProductionProcessDao productionProcessDao;

    private final ProductionMapper productionMapper;
    private final BlockchainService blockchainService;

    @Override
    public Long createProductionCert(ProductionCertDto certDto) {
        ProductionCertEntity entity = productionMapper.toProductionCertEntity(certDto);
        entity.setStatus(1);
        entity.setOnChain(false);
        entity = productionCertDao.save(entity);
        entity.setProductId(entity.getId().toString());
        return productionCertDao.save(entity).getId();
    }

    @Override
    public ProductionCertDto updateProductionCert(Long id,ProductionCertDto certDto) {
        Optional<ProductionCertEntity>  origin = productionCertDao.findById(id);
        if(!origin.isPresent())
            throw new RuntimeException("不存在此记录，id = "+id);
        ProductionCertEntity entity = productionMapper.toProductionCertEntity(certDto);
        entity.setId(id);
        entity.setProductId(String.valueOf(id));
        // 编辑后重新审核
        entity.setStatus(1);
        entity = productionCertDao.save(entity);
        return productionMapper.toProductionCertDto(entity);
    }

    @Override
    public ProductionCertDto updateProductionCertApply(Long id, int status) {
        Optional<ProductionCertEntity> origin = productionCertDao.findById(id);
        ProductionCertEntity entity = origin.get();
        entity.setStatus(status);
        entity.setId(id);
        entity.setProductId(String.valueOf(id));
        productionCertDao.save(entity);
        return this.getByIdProductionCert(id);
    }

    @Override
    public Long deleteProductionCert(Long id) {
        productionCertDao.deleteById(id);
        return id;
    }

    @Override
    public ProductionCertDto getByIdProductionCert(Long id) {
        Optional<ProductionCertEntity> res = productionCertDao.findById(id);
        ProductionCertEntity entity;
        if(res.isPresent())
            entity = res.get();
        else
            throw new RuntimeException("不存在此记录，id = "+id.toString());
        return productionMapper.toProductionCertDto(entity);
    }

    @Override
    public List<ProductionCertDto> getListProductionCert() {
        List<ProductionCertEntity> certEntityList = productionCertDao.findAll();
        return productionMapper.toProductionCertDtoList(certEntityList);
    }

    @Override
    public String createProductionWorkorder(ProductionWorkorderDto dto) {
        ProductionWorkorderEntity entity = productionMapper.toProductionWorkorderEntity(dto);
        entity.setOnChain(false);
        return productionWorkorderDao.save(entity).getWorkOrderNo();
    }

    @Override
    public ProductionWorkorderDto updateProductionWorkorder(String id, ProductionWorkorderDto dto) {
        ProductionWorkorderEntity entity = productionMapper.toProductionWorkorderEntity(dto);
        entity.setWorkOrderNo(id);
        entity = productionWorkorderDao.save(entity);
        return productionMapper.toProductionWorkorderDto(entity);
    }

    @Override
    public Long deleteProductionWorkorder(String id) {
        return productionWorkorderDao.deleteByWorkOrderNo(id);
    }

    @Override
    public ProductionWorkorderDto getByIdProductionWorkorder(String id) {
        ProductionWorkorderEntity res = productionWorkorderDao.findByWorkOrderNo(id);
        if(res == null)
            throw new RuntimeException("不存在此记录，id = "+id);
        return productionMapper.toProductionWorkorderDto(res);
    }

    @Override
    public ProductionWorkorderDto getByMOProductionWorkorder(String mo) {
        ProductionWorkorderEntity entity = productionWorkorderDao.findByPlanOrderNo(mo);
        if (entity == null){
            return null;
        }
        return productionMapper.toProductionWorkorderDto(entity);
    }

    @Override
    public List<ProductionWorkorderDto> getListProductionWorkorder() {
        List<ProductionWorkorderEntity> entities = productionWorkorderDao.findAll();
        return productionMapper.toProductionWorkorderDtoList(entities);
    }

    @Override
    public Long createProductionScheduling(ProductionSchedulingDto dto) {
        ProductionSchedulingEntity entity = productionMapper.toProductionSchedulingEntity(dto);
        if(entity.getCreateTime()==null)
            entity.setCreateTime(DateTimeUtils.getCurrentDateTime());
        entity.setOnChain(false);
        entity = productionSchedulingDao.save(entity);
        return entity.getId();
    }

    //前端传的dto数据是不全的，需要先从数据库中取出entity，再用dto更新entity
    @Override
    public ProductionSchedulingDto updateProductionScheduling(Long id, ProductionSchedulingDto dto) {
        ProductionSchedulingEntity entity = productionSchedulingDao.findById(id).get();
        productionMapper.updateProductionSchedulingEntity(entity,dto);
        entity = productionSchedulingDao.save(entity);
        return productionMapper.toProductionSchedulingDto(entity);
    }

    @Override
    public Long deleteProductionScheduling(Long id) {
        productionSchedulingDao.deleteById(id);
        return id;
    }

    @Override
    public ProductionSchedulingDto getByIdProductionScheduling(Long id){
        Optional<ProductionSchedulingEntity> res = productionSchedulingDao.findById(id);
        ProductionSchedulingEntity entity;
        if(res.isPresent())
            entity = res.get();
        else
            throw new RuntimeException("不存在此记录，id = "+id.toString());
        return productionMapper.toProductionSchedulingDto(entity);
    }

    @Override
    public List<ProductionSchedulingDto> getListByWOProductionScheduling(String wo) {
        ProductionWorkorderEntity woEntity = productionWorkorderDao.findByWorkOrderNo(wo);
        if (woEntity == null){
            throw new RuntimeException("schedule wo" + wo + " not exists");
        }
        List<ProductionSchedulingEntity> entities = productionSchedulingDao.findByWorkOrderNo(wo);

        return productionMapper.toProductionSchedulingDtoList(entities);
    }

    @Override
    public ProductionSchedulingDto getByBNProductionScheduling(String bn) {
        ProductionSchedulingEntity entity = productionSchedulingDao.findByBatchNumber(bn);
        if (entity == null){
            throw new RuntimeException("schedule bn" + bn + " not exists");
        }
        return productionMapper.toProductionSchedulingDto(entity);
    }

    @Override
    public List<ProductionSchedulingDto> getListProductionScheduling() {
        List<ProductionSchedulingEntity> entities = productionSchedulingDao.findAll();
        return productionMapper.toProductionSchedulingDtoList(entities);
    }

    @Override
    public Long createProductionProcess(ProductionProcessDto dto) {
        ProductionProcessEntity entity = productionMapper.toProductionProcessEntity(dto);
        if (entity.getNum()==null)
            entity.setNum(0);
        if (entity.getWeight()==null)
            entity.setWeight(0f);
        if (entity.getStatus()==null)
            entity.setStatus(1);
        entity.setOnChain(false);
        entity.setManufacture(entity.getEnterpriseName());
        entity = productionProcessDao.save(entity);
        return entity.getId();
    }

    @Override
    public ProductionProcessDto updateProductionProcess(Long id, ProductionProcessDto dto) throws Exception {
        Optional<ProductionProcessEntity> res = productionProcessDao.findById(id);
        ProductionProcessEntity entity;
        if(res.isPresent())
            entity = res.get();
        else
            throw new Exception("不存在此记录，id = "+id.toString());
        if (dto.getNum()==null)
            dto.setNum(entity.getNum());
        if (dto.getWeight()==null)
            dto.setWeight(entity.getWeight());
        if (dto.getStatus()==null)
            dto.setStatus(entity.getStatus());
        entity = productionMapper.toProductionProcessEntity(dto);
        entity.setId(id);
        entity = productionProcessDao.save(entity);
        return productionMapper.toProductionProcessDto(entity);
    }

    @Override
    public Long deleteProductionProcess(Long id) {
        productionProcessDao.deleteById(id);
        return id;
    }

    @Override
    public ProductionProcessDto getByIdProductionProcess(Long id) {
        Optional<ProductionProcessEntity> res = productionProcessDao.findById(id);
        ProductionProcessEntity entity;
        if(res.isPresent())
            entity = res.get();
        else
            throw new RuntimeException("不存在此记录，id = "+id.toString());
        return productionMapper.toProductionProcessDto(entity);
    }

    @Override
    public List<ProductionProcessDto> getListProductionProcess() {
        List<ProductionProcessEntity> entities = productionProcessDao.findAll();
        return productionMapper.toProductionProcessDtoList(entities);
    }

    @Override
    public ProductionSchedulingDto finishScheduling(Long id) {
        ProductionSchedulingEntity entity = productionSchedulingDao.findById(id).get();
        int quantity = entity.getScheduledQuantity();
        entity.setReportedQuantity(quantity);
        entity.setWorkOrderStatus("completed");
        entity = productionSchedulingDao.save(entity);
        ProductionSchedulingDto dto = productionMapper.toProductionSchedulingDto(entity);
        this.reportToWorkOrder(dto);
        return dto;
    }

    @Override
    public ProductionWorkorderDto reportToWorkOrder(ProductionSchedulingDto schedulingDto) {
        ProductionWorkorderEntity entity = productionWorkorderDao.findByWorkOrderNo(schedulingDto.getWorkOrderNo());
        int quantity = entity.getReportedQuantity() + schedulingDto.getReportedQuantity();
        entity.setReportedQuantity(quantity);
        productionWorkorderDao.save(entity);
//        if (quantity == entity.getWorkOrderQuantity()){
//            this.finishWorkOrder(entity.getWorkOrderNo());
//        }
        return productionMapper.toProductionWorkorderDto(entity);
    }

    @Override
    public ProductionWorkorderDto finishWorkOrder(String id) {
        ProductionWorkorderEntity entity = productionWorkorderDao.findByWorkOrderNo(id);
        ProductionWorkorderDto dto = productionMapper.toProductionWorkorderDto(entity);
        planService.reportToPlanOrder(dto);
        return dto;
    }

    @Override
    public UploadInfo uploadProductionCert(Long id) {
        ProductionCertDto productionCert = this.getByIdProductionCert(id);
        if (null!=productionCert){
            if (0!=productionCert.getStatus())
                throw new RuntimeException("cert not reviewed");
            UploadInfo txInfo = blockchainService.uploadProductionCert("ProdCert"+id, productionCert.toString());
            productionCert.setOnChain(true);
            productionCert.setCommitTime(txInfo.getCommitTime());
            productionCert.setTransactionId(txInfo.getTransactionId());
            productionCertDao.save(productionMapper.toProductionCertEntity(productionCert));
            return txInfo;
        }
        throw new RuntimeException("production cert not found");
    }

    @Override
    public UploadInfo uploadProductionWorkorder(String id) {
        ProductionWorkorderDto workorder = this.getByIdProductionWorkorder(id);
        if (null!=workorder){
            UploadInfo txInfo = blockchainService.uploadProductionOrder(id, workorder.toString());
            workorder.setOnChain(true);
            workorder.setCommitTime(txInfo.getCommitTime());
            workorder.setTransactionId(txInfo.getTransactionId());
            productionWorkorderDao.save(productionMapper.toProductionWorkorderEntity(workorder));
            return txInfo;
        }
        throw new RuntimeException("production work order not found");
    }

    @Override
    public UploadInfo uploadProductionScheduling(Long id) {
        ProductionSchedulingDto scheduling = this.getByIdProductionScheduling(id);
        if (null!=scheduling){
            if (scheduling.getBatchNumber() == null)
                throw new RuntimeException("scheduling not finished");
            UploadInfo txInfo = blockchainService.uploadProductionSchedule(scheduling.getBatchNumber(), scheduling.toString());
            scheduling.setOnChain(true);
            scheduling.setCommitTime(txInfo.getCommitTime());
            scheduling.setTransactionId(txInfo.getTransactionId());
            productionSchedulingDao.save(productionMapper.toProductionSchedulingEntity(scheduling));
            return txInfo;
        }
        throw new RuntimeException("production scheduling not found");
    }

    @Override
    public UploadInfo uploadProductionProcess(Long id) {
        ProductionProcessDto process = this.getByIdProductionProcess(id);
        if (null!=process){
            if (2!=process.getStatus())
                throw new RuntimeException("process or traceCode not reviewed");
            UploadInfo txInfo = blockchainService.uploadProductionProcess("ProdProc"+id, process.toString());
            process.setOnChain(true);
            process.setCommitTime(txInfo.getCommitTime());
            process.setTransactionId(txInfo.getTransactionId());
            productionProcessDao.save(productionMapper.toProductionProcessEntity(process));
            return txInfo;
        }
        throw new RuntimeException("production process not found");
    }

    @Override
    public List<ProductionCertDto> getListProductionCertByEid(String eid) {
        List<ProductionCertEntity> certEntityList = productionCertDao.findAllByEnterpriseId(eid);
        return productionMapper.toProductionCertDtoList(certEntityList);
    }

    @Override
    public List<ProductionWorkorderDto> getListProductionWorkorderByEid(String eid) {
        List<ProductionWorkorderEntity> entities = productionWorkorderDao.findAllByEnterpriseId(eid);
        return productionMapper.toProductionWorkorderDtoList(entities);
    }

    @Override
    public List<ProductionSchedulingDto> getListProductionSchedulingByEid(String eid) {
        List<ProductionSchedulingEntity> entities = productionSchedulingDao.findAllByEnterpriseId(eid);
        return productionMapper.toProductionSchedulingDtoList(entities);
    }

    @Override
    public List<ProductionProcessDto> getListProductionProcessByEid(String eid) {
        List<ProductionProcessEntity> entities = productionProcessDao.findAllByEnterpriseId(eid);
        return productionMapper.toProductionProcessDtoList(entities);
    }
}
